import os.path

from engine.backtest import Backtest

from engine.strategy import Strategy
from engine.algos import *
from logic.config import Config,Task
from engine.data.data_handler import DataHandler

import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['KaiTi']  # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False

import pandas as pd
from logic.global_objs import D
from logic.global_objs import g as G
import threading


class MainLogic:
    def __init__(self,win):
        self.win = win

    def load_data(self, codes, start='20100101',end='20211231', fields=[], days_future=5,split_count=10):
        fields_all = fields.copy()
        names_all = names.copy()


        if days_future <= 1:
            label_field = 'Ref($close,-1)/$close -1'
        else:
            label_field = 'Ref($close,-{})/Ref($close,-{}) -1'.format(days_future,days_future-1)

        if continue_label:
            fields_all.append(label_field)
            names_all.append('label')
        else:
            fields_all.append(label_field)
            names_all.append('label_value')
            fields.append('QCut($label_value,{}})'.format(split_count))
            names.append('label')

        config_path = Config.get_config_path()
        store = pd.HDFStore('{}/{}'.format(config_path, 'cache.h5'))
        key_to_store = 'cached_df'
        if '/' + key_to_store in store.keys():
            df = store.get(key=key_to_store)
        else:


            '''
            
            feature_name = [
                #('PairSlope($high, $close,18)','RSRS'),
                #('$close / Ref($close,20) -1 ','20日动量'),
                #('$20日动量>=0.08','to_buy_momentum'),
                #('$20日动量<0.0', 'to_sell_momentum'),
                #('$RSRS>=0.08', 'to_buy_RSRS'),
                #('$RSRS<0.0', 'to_sell_RSRS'),
                ,
            ]
            '''
            print(names_all)
            df = D.load(codes=codes,fields=fields_all, names=names_all,start_time=start,end_time=end)

        return df

    def prepare_data(self, task: Task):
        features = task.list_features
        if task.enum_task_type == 1:
            df = D.load(task.list_codes, start_time=task.date_start, end_time=task.date_end)
        elif task.enum_task_type == 2:
            fields = task.list_buy_rules + task.list_sell_rules + task.list_features
            df = D.load(task.list_codes, start_time=task.date_start, end_time=task.date_end, fields=fields, names=fields)
            buy_temp = df[task.list_buy_rules].sum(axis=1)
            df['to_buy'] = buy_temp >= task.buy_rules_min_count
            sell_temp = df[task.list_sell_rules].sum(axis=1)
            df['to_sell'] = sell_temp >= task.sell_rules_min_count
        elif task.enum_task_type == 3:
            fields = task.list_features
            names = fields

            all_names = names.copy()
            all_fields = fields.copy()

            all_fields.append('Ref($close,-5)/$close -1')
            all_names.append('label')

            all_fields.append('QCut($label,5)')
            all_names.append('label_c')
            df = D.load(task.list_codes, start_time=task.date_start, end_time=task.date_end, fields=all_fields, names=all_names)
            from engine.model.ml_models import MLModels
            model = MLModels(task.name)
            df['pred_score'] = model.predict(df[fields], df['label_c'])
        return df

        '''
        
        from engine.data.data_handler import DataHandler
        f,n = DataHandler().get_kbar_fields_names()
        fields += f
        names += n

        '''


        '''
        

        fields_all = fields.copy()
        fields_all.append('Ref($close,-5)/$close -1')
        fields_all.append('QCut($label_values,10)')

        names_all = names.copy()
        names_all.append('label_values')
        names_all.append('label')

        df = self.load_data(codes=config['codes'], start=config['start'], end=config['end'], fields=fields_all, names=names_all)
        
        return df
        '''

    def train_model(self,df,names):
        from engine.model.gbdt import LGBModel
        m = LGBModel()
        m.fit(df, fields=names, train_valid_date='20160101')
        results = m.predict()
        df['pred_score'] = results

    def run(self, task):
        thread = threading.Thread(target=self.run_strategy_by_task, args=[task])
        thread.start()

    def show_ui_message(self,message):
        G.notify(G.MESSAGE,{'message':message})

    def run_strategy_by_task(self, task:Task):
        self.show_ui_message('启动回测...\n开始日期:{},结束日期：{}'.format(task.date_start, task.date_end))

        self.task = task

        freq_algo_list = [None, RunPeriod(5), RunPeriod(22), RunPeriod(66), RunPeriod(252), RunOnce()]
        select_type_list = [SelectAll(),SelectBySignal(), None] #机器模型不需要选股，设为None

        freq_algo = None
        freq_algo = freq_algo_list[task.enum_freq - 1] #从1开始转为从0开始

        select_algo = None
        select_algo = select_type_list[task.enum_task_type - 1]

        weight_alog_list = [WeightEqually(), WeightFix(weights=[task.weight_fix])]
        weight_algo = None
        weight_algo = weight_alog_list[task.enum_weight - 1]

        order_by_algo = None
        if task.enum_task_type == 3: #机器模型
            order_by_algo = SelectTopK(K=1, col='pred_score')
        elif task.enum_task_type == 2:
            if task.orderby != '':
                order_by_algo = SelectTopK(K=1, col=task.orderby)

        algos_list = []
        if freq_algo:
            algos_list.append(freq_algo)
        if select_algo:
            algos_list.append(select_algo)
        if order_by_algo:
            algos_list.append(order_by_algo)
        if weight_algo:
            algos_list.append(weight_algo)

        self.show_ui_message('\n策略算子列表：')
        for algo in algos_list:
            self.show_ui_message(str(algo))
            self.show_ui_message('\n')

        codes = task.list_codes

        s = Strategy(task.name, algo_list=algos_list)

        df = self.prepare_data(task)
        self.show_ui_message(str(list(df.columns)))
        b = Backtest(df=df)
        df = b.run(s)
        df.to_csv(Config.get_config_path() +'/results/{}.csv'.format(s.name))
        #self.graphs(df)
        return df


    def analysis(self, start, end, benchmarks=[], task_names=[]):
        equities = []
        for benchmark in benchmarks:
            bench_df = D.get_code_df(benchmark)[start:end]
            se = (bench_df['rate']+1).cumprod()
            se.name = benchmark
            equities.append(se)

        for name in task_names:
            filename = Config.get_config_path()+'/results/{}.csv'.format(name)
            if os.path.exists(filename):
                df = pd.read_csv(filename)
                df['date'] = df['date'].apply(lambda x:str(x))
                df.index = df['date']
                se =(df['rate']+1).cumprod()
                se.name = name
                equities.append(se)

        df_equities = pd.concat(equities,axis=1)
        df_equities.dropna(inplace=True)
        print(df_equities)

        from engine.performance import PerformanceUtils

        df_ratios, df_corr, df_years = PerformanceUtils().calc_equity(df_equity=df_equities)
        G.notify(G.GRAPH_RESULT,
                 {
                     'equity': df_equities,
                     'ratio': df_ratios,
                     'corr': df_corr,
                     'yearly': df_years

                 })

        if True:
            print(df_ratios)
            #df_equities.plot()
            #import matplotlib.pyplot as plt
            #plt.show()
        else:
            from logic.global_objs import g
            g.notify(g.MSG_TYPE_SERIES,
                     {
                        'plot':df_plot,
                         'ratio':df_ratios,
                         'corr':df_corr,
                         'yearly':df_years

                     })


    def plotly(self,dict_results):
        from gui.graph.plotly_graph import SubplotsGraph
        g = SubplotsGraph(2, 2)
        g.add_graph('line',dict_results['plot'],1,1)
        #g.add_graph('table',dict_results['ratio'],1,2)
        g.show()

if __name__ == '__main__':
    #fields, names = DataHandler().get_kbar_fields_names()
    #fields = ['$open', '$high', '$low', '$close']
    #names = ['open', 'high', 'low', 'close']
    #fields.append('Ref($close,-2)/Ref($close,-1) -1')
    #names.append('label')

    logic = MainLogic(win=None)
    logic.analysis('20100101','20211128',benchmarks=['000300.SH'], task_names=['买入并持有-沪深300'])

